package tech.lq0.dreamaticvoyage.block.entity;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.WorldlyContainer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.wrapper.InvWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import tech.lq0.dreamaticvoyage.capability.ModCapabilities;
import tech.lq0.dreamaticvoyage.capability.uce.UCEnergyStorage;
import tech.lq0.dreamaticvoyage.init.ItemRegistry;

import java.util.concurrent.atomic.AtomicBoolean;

public abstract class AbstractPylonBlockEntity extends AbstractUCEProviderBlockEntity implements WorldlyContainer {

    // 一块燃料能提供的充能时长
    public static final int CHARGE_TIME = 320;

    // 最大UCE容量
    protected final int capacity;
    // 自身产生UCE速度 (单位 UCE/tick)
    protected final int chargeSpeed;
    // 充能间隔 (单位 tick)
    protected final int transferCooldown;

    protected LazyOptional<UCEnergyStorage> energyHandler;
    public int chargeTime;
    public int cooldown = 0;

    private LazyOptional<?> itemHandler = LazyOptional.of(() -> new InvWrapper(this));
    protected NonNullList<ItemStack> items = NonNullList.withSize(1, ItemStack.EMPTY);

    public AbstractPylonBlockEntity(BlockEntityType<?> pType, BlockPos pPos, BlockState pBlockState, Builder builder) {
        super(pType, pPos, pBlockState, builder.range, builder.maxConnectCount);

        this.capacity = builder.capacity;
        this.chargeSpeed = builder.chargeSpeed;
        this.transferCooldown = builder.transferCooldown;

        this.energyHandler = LazyOptional.of(() -> new UCEnergyStorage(builder.capacity));
    }

    protected void selfCharge(Level pLevel, BlockPos pPos, BlockState pState) {
        AtomicBoolean flag = new AtomicBoolean(false);
        this.energyHandler.ifPresent(handler -> flag.set(handler.getEnergyStored() >= handler.getMaxEnergyStored()));
        if (flag.get()) return;

        if (this.chargeTime <= 0) {
            ItemStack stack = this.items.get(0);
            if (stack.isEmpty()) {
                return;
            }

            if (!stack.is(ItemRegistry.SWOLLEN_FUKAMIZU_BREAD_INGOT.get())) {
                return;
            }

            stack.shrink(1);
            this.chargeTime += CHARGE_TIME;

            this.setChanged();
            pLevel.sendBlockUpdated(pPos, pState, pState, 3);
            pLevel.gameEvent(GameEvent.BLOCK_CHANGE, pPos, GameEvent.Context.of(pState));
        } else {
            this.energyHandler.ifPresent(handler -> handler.receiveEnergy(this.chargeSpeed, false));
            this.chargeTime--;
        }
    }

    /**
     * 自动移除失效链接
     */
    public void refreshConnections(Level pLevel, BlockPos pPos) {
        this.connections.removeIf(offset -> {
            var newPos = new BlockPos(pPos.getX() + offset[0], pPos.getY() + offset[1], pPos.getZ() + offset[2]);
            var blockEntity = pLevel.getBlockEntity(newPos);
            return blockEntity == null || !blockEntity.getCapability(ModCapabilities.UMISU_CURRENT_ENERGY_CAPABILITY).isPresent();
        });
    }

    protected void transferEnergy(Level pLevel, BlockPos pPos) {
        if (this.cooldown > 0) {
            this.cooldown--;
        } else {
            this.cooldown = this.transferCooldown;
        }

        if (this.cooldown == 0 && this.energyHandler.map(UCEnergyStorage::getEnergyStored).orElse(0) > 0) {
            // 尝试发送能量
            int count = this.connections.size();
            this.connections.forEach(offset -> {
                var newPos = new BlockPos(pPos.getX() + offset[0], pPos.getY() + offset[1], pPos.getZ() + offset[2]);
                var blockEntity = pLevel.getBlockEntity(newPos);
                if (blockEntity == null) return;

                blockEntity.getCapability(ModCapabilities.UMISU_CURRENT_ENERGY_CAPABILITY).ifPresent(targetBlockHandler -> {
                    if (targetBlockHandler.canReceive() && targetBlockHandler.getEnergyStored() < targetBlockHandler.getMaxEnergyStored()) {
                        this.energyHandler.ifPresent(handler -> {
                            int energy = targetBlockHandler.receiveEnergy(Math.min(handler.getEnergyStored(), this.capacity / count), false);
                            handler.extractEnergy(energy, false);
                        });
                    }
                });
            });
            this.setChanged();
        }
    }

    abstract public int getPylonLevel();

    @Override
    boolean canBeCharged(BlockEntity energySource) {
        return energySource instanceof AbstractPylonBlockEntity blockEntity && blockEntity.getPylonLevel() > this.getPylonLevel();
    }

    @Override
    public int[] getSlotsForFace(Direction pSide) {
        return new int[]{0};
    }

    @Override
    public boolean canPlaceItemThroughFace(int pIndex, ItemStack pItemStack, @Nullable Direction pDirection) {
        return pDirection != Direction.DOWN && pItemStack.is(ItemRegistry.SWOLLEN_FUKAMIZU_BREAD_INGOT.get());
    }

    @Override
    public boolean canTakeItemThroughFace(int pIndex, ItemStack pStack, Direction pDirection) {
        return false;
    }

    @Override
    public int getContainerSize() {
        return this.items.size();
    }

    @Override
    public boolean isEmpty() {
        for (ItemStack itemstack : this.items) {
            if (!itemstack.isEmpty()) {
                return false;
            }
        }

        return true;
    }

    @Override
    public ItemStack getItem(int pSlot) {
        return this.items.get(pSlot);
    }

    @Override
    public ItemStack removeItem(int pSlot, int pAmount) {
        return ContainerHelper.removeItem(this.items, pSlot, pAmount);
    }

    @Override
    public ItemStack removeItemNoUpdate(int pSlot) {
        return ContainerHelper.takeItem(this.items, pSlot);
    }

    @Override
    public void setItem(int pSlot, ItemStack pStack) {
        ItemStack itemstack = this.items.get(pSlot);
        boolean flag = !pStack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, pStack);
        this.items.set(pSlot, pStack);
        if (pStack.getCount() > this.getMaxStackSize()) {
            pStack.setCount(this.getMaxStackSize());
        }

        if (pSlot == 0 && !flag) {
            this.setChanged();
        }
    }

    @Override
    public boolean stillValid(Player pPlayer) {
        return Container.stillValidBlockEntity(this, pPlayer);
    }

    @Override
    public void clearContent() {
        this.items.clear();
    }

    @Override
    public void load(CompoundTag pTag) {
        super.load(pTag);

        if (pTag.contains("UmisuEnergy")) {
            getCapability(ModCapabilities.UMISU_CURRENT_ENERGY_CAPABILITY).ifPresent(handler -> ((UCEnergyStorage) handler).deserializeNBT(pTag.get("UmisuEnergy")));
        }
        this.chargeTime = pTag.getInt("ChargeTime");
        this.cooldown = pTag.getInt("Cooldown");
        this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
        ContainerHelper.loadAllItems(pTag, this.items);
    }

    @Override
    protected void saveAdditional(CompoundTag pTag) {
        super.saveAdditional(pTag);

        getCapability(ModCapabilities.UMISU_CURRENT_ENERGY_CAPABILITY).ifPresent(handler -> pTag.put("UmisuEnergy", ((UCEnergyStorage) handler).serializeNBT()));
        pTag.putInt("ChargeTime", this.chargeTime);
        pTag.putInt("Cooldown", this.cooldown);
        ContainerHelper.saveAllItems(pTag, this.items);
    }

    @Override
    public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
        if (cap == ModCapabilities.UMISU_CURRENT_ENERGY_CAPABILITY) {
            return this.energyHandler.cast();
        }
        if (!this.remove && side != Direction.DOWN && cap == ForgeCapabilities.ITEM_HANDLER) {
            return this.itemHandler.cast();
        }
        return super.getCapability(cap, side);
    }

    @Override
    public void invalidateCaps() {
        super.invalidateCaps();
        this.energyHandler.invalidate();
        this.itemHandler.invalidate();
    }

    @Override
    public void reviveCaps() {
        super.reviveCaps();
        this.energyHandler = LazyOptional.of(() -> new UCEnergyStorage(this.capacity));
        this.itemHandler = LazyOptional.of(() -> new InvWrapper(this));
    }

    public static class Builder {

        int range = 16;
        int capacity = 30000;
        int chargeSpeed = 32;
        int transferCooldown = 40;
        int maxConnectCount = 10;

        public Builder range(int range) {
            this.range = Mth.clamp(range, 0, 127);
            return this;
        }

        public Builder capacity(int capacity) {
            this.capacity = Math.max(0, capacity);
            return this;
        }

        public Builder chargeSpeed(int chargeSpeed) {
            this.chargeSpeed = Math.max(0, chargeSpeed);
            return this;
        }

        public Builder transferCooldown(int transferCooldown) {
            this.transferCooldown = Math.max(0, transferCooldown);
            return this;
        }

        public Builder maxConnectCount(int maxConnectCount) {
            this.maxConnectCount = Math.max(0, maxConnectCount);
            return this;
        }
    }
}
